﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Acl.DataCheck;

/// <summary>
/// 范围约束检验器
/// </summary>
internal class RangeValidator : ConstraintValidator
{
    private static Dictionary<int, Tuple<string, List<dynamic>>> _caches = [];

    public static List<dynamic> ParseRange(ConstraintItem constraint, StringBuilder log)
    {
        var expression = constraint.Expression;

        var key = HashCode.Combine(expression);

        if (!_caches.TryGetValue(key, out var tuple))
        {
            _caches[key] = tuple = Parse(constraint);
        }

        if (!string.IsNullOrEmpty(tuple.Item1))
        {
            log.AppendLine(tuple.Item1);
            return default;
        }

        return tuple.Item2;
    }

    private static Tuple<string, List<dynamic>> Parse(ConstraintItem constraint)
    {
        var tokens = ParseToken(constraint.Expression);
        var length = tokens.Length;
        if (length == 0) return new Tuple<string, List<dynamic>>($"Range 表达式不能为空。", null);
        if (length < 4) return new Tuple<string, List<dynamic>>($"Range 表达式格式错误。", null);

        if ((tokens[0][0] is 'i' or 'I') &&
            (tokens[0][1] is 'n' or 'N') &&
            (tokens[1] is "(" or "（") &&
            (tokens[length - 1]) is ")" or "）")
        {
            var range = new List<dynamic>(length - 3);
            for (var i = 2; i < length - 1; i++)
                range.Add(tokens[i]);

            return new Tuple<string, List<dynamic>>(null, range);
        }

        return new Tuple<string, List<dynamic>>($"Range 表达式格式错误。", null);
    }

    internal bool IsFlagEnum { get; set; }

    public override void Validate(ValidationContext ctx)
    {
        Guard.NotNull(ctx, nameof(ctx));

        if (IsFlagEnum && ctx.Current.Type != ConstraintType.FlagEnum)
            throw new NotSupportedException($"{GetType().Name} 不支持 '{ctx.Current.Type}' ,出错相关信息:{ctx.Current} ");

        if (!IsFlagEnum && ctx.Current.Type != ConstraintType.Range)
            throw new NotSupportedException($"{GetType().Name} 不支持 '{ctx.Current.Type}' ,出错相关信息:{ctx.Current} ");

        if (ctx.Current.Enabled)
        {
            if (!ctx.Tables.Contains(ctx.Current.TableName))
                throw new NotSupportedException($"{ctx.Current.TableName} 表名不存在 ,出错相关信息:{ctx.Current} ");

            var table = ctx.Tables[ctx.Current.TableName];
            if (!table.Members.Contains(ctx.Current.ColumnName))
                throw new NotSupportedException($"{ctx.Current.TableName}.{ctx.Current.ColumnName} 列不存在 ,出错相关信息:{ctx.Current} ");

            Type columnType = table.Members[ctx.Current.ColumnName].Type;

            //解析表达式
            var range = ParseRange(ctx.Current, ctx.Logger);
            ctx.Current.Passed = range != null;

            if (ctx.Current.Passed)
            {
                if (IsFlagEnum)
                {
                    range = range.Select(x => Converter.Convert(x, typeof(long))).ToList();
                }
                else
                {
                    range = range.Select(x => Converter.Convert(x, columnType)).ToList();
                }

                var strRange = string.Join(",", range);

                foreach (dynamic row in table)
                {
                    if (IsFlagEnum)
                    {
                        var columnValue = row.Field<long>(ctx.Current.ColumnName);
                        var passed = false;
                        foreach (var item in range)
                        {
                            if ((columnValue & item) == item)
                            {
                                passed = true;
                                break;
                            }
                        }

                        if (!passed)
                        {
                            ctx.Logger.AppendLine($"{ctx.Current.TableName}.{ctx.Current.ColumnName}={columnValue} 值在 ({strRange})标记枚举中不存在，记录信息:{row}");
                            ctx.Current.Passed = false;
                        }
                    }
                    else
                    {
                        var columnValue = row[ctx.Current.ColumnName];
                        if (!range.Contains(columnValue))
                        {
                            //
                            ctx.Logger.AppendLine($"{ctx.Current.TableName}.{ctx.Current.ColumnName}={columnValue} 值在 ({strRange}) 中不存在，记录信息:{row}");
                            ctx.Current.Passed = false;
                        }
                    }
                }
            }
        }
    }
}